#include <thread>
#include <string>
#include <list>
#include <chrono>
#include <sstream>
#include <mutex>
#include <fstream>
#include <omp.h>
#include <boost/locale.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <unordered_map>
#include "common/termcolor.hpp"

#define CHUNKS 600000
std::ifstream ifs;
std::mutex mtx,mfx;
std::string filename = "chunk.dat";
std::unordered_map<std::wstring,int> deserializeMapFromChunks() {
    std::unordered_map<std::wstring,int> result;
    if (!ifs.is_open()) {
         ifs.open(filename, std::ios::binary);
    }
    try{
        std::lock_guard<std::mutex> lock(mfx);
        boost::archive::binary_iarchive ia(ifs);
        ia >> result;

        if(result.size()<CHUNKS)
            ifs.close();
    }catch(...){
        std::wcout<<"deserialize fail."<<std::endl;
    }
    return result;
}
void highlightMatches(const std::wstring& text, const std::wstring& match) {
    size_t pos = 0,found=0;
    while ((found = text.find(match, pos)) != std::string::npos) {
        std::wcout << text.substr(pos, found - pos);
        std::wcout << termcolor::red << match << termcolor::reset;
        pos = found + match.length();
    }
    std::wcout << text.substr(pos) << std::endl;
}
std::list<std::wstring> search_file(std::wstring filepath){
    std::list<std::wstring> result;
    while(true){
        std::unordered_map<std::wstring,int> hash;
        hash = deserializeMapFromChunks();
        
        int numThreads = omp_get_max_threads();
        size_t chunkSize = hash.size()/numThreads;
        #pragma omp parallel shared(result)
        {
            int id = omp_get_thread_num();
            auto start = hash.begin();
            std::advance(start, id * chunkSize);
            auto end = start;
            if(id==numThreads-1)
                end = hash.end();
            else
                std::advance(end, chunkSize);

            for (auto it = start; it != end; ++it){
                if(it->second>=filepath.size()&&it->first.find(filepath)!=std::string::npos){
                    result.emplace_back(it->first);
                }
            }
        }
        if(hash.size()<CHUNKS) break;
    }
    return result;
}
int main(int argc, char** argv){
    if(argc>1){
        std::setlocale(LC_ALL, "");
        std::wcout.imbue(std::locale(""));
        for(int i = 1;i<argc;++i){
            std::wstring s = boost::locale::conv::to_utf<wchar_t>(argv[i], "gbk");
            auto ret = search_file(s);
            for(auto it:ret)
                highlightMatches(it,s);
        }
    }else{
        std::wcout<<"Usage: search.exe <filename>"<<std::endl;
    }
}